#ifndef PW_PBL_CONCEPTS_PACK_HPP
#define PW_PBL_CONCEPTS_PACK_HPP

#include <tuple>
#include "Core.hpp"
#include "Sequence.hpp"

namespace pw_pbl::inline Concepts
{
	template <intptr_t Index, typename... T>
	using ttPackElement = T...[Index < 0 ? sizeof...(T) + Index : Index];

	template <typename T>
	inline constexpr size_t vTupleSize = 0;

	template <typename... T, template <typename...> class TEMPLATE>
	inline constexpr size_t vTupleSize<TEMPLATE<T...>> = sizeof...(T);

	template <typename FUNC, typename... PARMS> requires std::invocable<FUNC, PARMS...>
	struct ttProxyFunction
	{
		FUNC func;
		decltype(auto) operator()(PARMS... parms) { return func(std::forward<PARMS>(parms)...); }
	};

	template <template <typename...> class OUT, template <size_t, typename> class GEN, typename... P>
	struct _ttEachApplyWithId
	{
		template <typename SEQ>
		struct imp;

		template <size_t... ID>
		struct imp<std::index_sequence<ID...>>
		{
			using type = OUT<GEN<ID, P>...>;
		};
		using type = typename imp<std::make_index_sequence<sizeof...(P)>>::type;
	};
	/// 生成OUT<GEN<ID,P>...>类型，其中ID为P在对应参数包中的索引
	template <template <typename...> class OUT, template <size_t, typename> class GEN, typename... P>
	using ttEachApplyWithId = typename _ttEachApplyWithId<OUT, GEN, P...>::type;

	template <std::integral T, T, T, T>
	struct _ttSequenceRange;

	template <typename TUPLE, typename SEQ>
	struct _ttSequenceApplyTuple;

	template <auto... PARMS>
	struct _ttSequenceGen;

	template <typename TUPLE, intptr_t... RANGE>
	using _ttSliceTuple = _ttSequenceApplyTuple<typename _ttSequenceRange<intptr_t, RANGE...>::type, TUPLE>;

	/// 返回tuple的 [BEGIN,LIMIT) 半开区间（当BEGIN > LIMIT时以反向顺序列出）的切片tuple
	template <typename TUPLE, intptr_t BEGIN, intptr_t LIMIT = vTupleSize<TUPLE>, intptr_t STEP = 1>
	using ttSliceTuple = typename _ttSliceTuple<TUPLE, BEGIN, LIMIT, STEP>::type;

	template <typename TUPLE, typename SEQ>
	using ttTupleSelectElements = typename _ttSequenceApplyTuple<SEQ, TUPLE>::type;

	template <typename... T>
	struct ttPack;

	template <typename T, template <typename...> class... TEMPLATE>
	struct ttTupleTraits;

	template <typename... T, template <typename...> class TEMPLATE>
	struct ttTupleTraits<TEMPLATE<T...>>
	{
		static constexpr auto ESizeofPack = sizeof...(T);

		using type = TEMPLATE<T...>;

		using pack = ttPack<T...>;

		/// 根据生成的序列选择参数然后应用回原模板
		template <auto... GEN>
		using select = ttTupleSelectElements<
			TEMPLATE<T...>,
			ttSelectAnyOne<
				_ttSequenceGen<GEN...>,
				_ttSequenceGen<GEN..., std::integral_constant<size_t, ESizeofPack>{}>,
				_ttSequenceGen<GEN..., ESizeofPack>>>;

		template <intptr_t INDEX>
		using element = ttPackElement<INDEX, T...>;

		template <typename... P>
		using append = TEMPLATE<T..., P...>;

		template <typename... P>
		using prepend = TEMPLATE<P..., T...>;

		template <typename... P>
		using replace_with = TEMPLATE<P...>;

		/// 对参数切片选择然后应用回原模板
		template <intptr_t BEGIN, intptr_t LIMIT = sizeof...(T), intptr_t STEP = 1>
		using slice = ttSliceTuple<TEMPLATE<T...>, BEGIN, LIMIT, STEP>;

		/// 对参数反向排列然后应用回原模板
		template <intptr_t BEGIN = -1, intptr_t LIMIT = -intptr_t(sizeof...(T)) - 1, intptr_t STEP = 1>
		using reverse = ttSliceTuple<TEMPLATE<T...>, BEGIN, LIMIT, STEP>;

		template <template <typename...> class OTHER>
		using apply = OTHER<T...>;

		template <template <typename> class OTHER>
		using each_apply = ttPack<OTHER<T>...>;

		template <template <size_t, typename> class GEN>
		using each_apply_with_index = ttEachApplyWithId<TEMPLATE, GEN, T...>;

		/// 生成将函数对象封包成以参数包为参数的函数
		template <typename FUNC>
		static auto proxy_function(FUNC &&func) noexcept
		{
			return ttProxyFunction<FUNC, T...>{.func = std::forward<FUNC>(func)};
		}
	};

	template <typename... T>
	struct ttPack : public ttTupleTraits<ttPack<T...>>
	{};

	template <typename... T, template <typename...> class TEMPLATE>
	struct ttTupleTraits<TEMPLATE<T...>, TEMPLATE> : public ttTupleTraits<TEMPLATE<T...>>
	{
	};

	template <intptr_t Index, typename TUPLE>
	using ttTupleElement = typename ttTupleTraits<TUPLE>::template element<Index>;

	template <typename TUPLE, typename... APPEND>
	using ttTupleAppend = typename ttTupleTraits<TUPLE>::template append<APPEND...>;

	template <typename TUPLE, typename... APPEND>
	using ttTuplePrepend = typename ttTupleTraits<TUPLE>::template prepend<APPEND...>;

	template <typename TUPLE, typename... P>
	using ttTuplePackReplace = typename ttTupleTraits<TUPLE>::template replace_with<P...>;

	template <typename... TUPLE>
	struct _ttTupleCat;

	template <typename... T, template <typename...> class TEMPLATE>
	struct _ttTupleCat<TEMPLATE<T...>>
	{
		using type = ttPack<T...>;
	};

	template <typename... T1, template <typename...> class TEMPLATE1, typename... T2, template <typename...> class TEMPLATE2, typename... TN>
	struct _ttTupleCat<TEMPLATE1<T1...>, TEMPLATE2<T2...>, TN...>
	{
		using type = typename _ttTupleCat<ttPack<T1..., T2...>, TN...>::type;
	};

	template <typename... TUPLE>
	using ttTupleCat = typename _ttTupleCat<TUPLE...>::type;

} // namespace pw_pbl::inline Concepts

#endif // PW_PBL_CONCEPTS_PACK_HPP
